package middleware

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

/*
 * ------------------------------------
 *       构建POST GET PUT三种方法
 * ------------------------------------
 */
type FuncVar struct {
	IP       string
	Port     string
	API      string `json:"api,omitempty"`
	User     string `json:"user,omitempty"`
	Password string `json:"password,omitempty"`
	Token    string `json:"token,omitempty"`
}

func postData(url FuncVar, d []byte) *http.Response { // POST方法函数

	targetUrl := "http://" + url.IP + ":" + url.Port + url.API // 目标url
	log.Printf("POST TARGET: %s \n", targetUrl)

	req, err := http.NewRequest("POST", targetUrl, bytes.NewBuffer(d))
	if err != nil {
		log.Printf("ERROR: Create HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}

	defer req.Body.Close()                                           // 必须设定该参数,POST参数才能正常提交
	req.Header.Set("Content-Type", "application/json;charset=UTF-8") // 请求头，意思是以json串提交数据

	if url.Token != "" { // basic auth 认证
		req.Header.Set("Authorization", "Bearer "+url.Token)
	} else if (url.User != "") && (url.Password != "") {
		req.SetBasicAuth(url.User, url.Password)
	}

	response, err := http.DefaultClient.Do(req) // 响应
	if err != nil {
		log.Printf("ERROR: Send HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}
	return response
}

func getData(url FuncVar) *http.Response { // GET方法函数
	targetUrl := "http://" + url.IP + ":" + url.Port + url.API // 目标url
	log.Printf("GET TARGET: %s \n", targetUrl)

	req, err := http.NewRequest("GET", targetUrl, nil)
	if err != nil {
		log.Printf("ERROR: Create HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}

	if url.Token != "" {
		req.Header.Set("Authorization", "Bearer "+url.Token)
	} else if (url.User != "") && (url.Password != "") {
		req.SetBasicAuth(url.User, url.Password)
	}

	response, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Printf("ERROR: Send HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}
	return response
}

func putData(url FuncVar, d []byte) *http.Response { //PUT方法函数

	targetUrl := "http://" + url.IP + ":" + url.Port + url.API // 带header的post方法
	log.Printf("PUT TARGET: %s \n", targetUrl)

	data := strings.NewReader(string(d))
	var req *http.Request

	if data == nil {
		req, _ = http.NewRequest("PUT", targetUrl, nil)
	} else {
		req, _ = http.NewRequest("PUT", targetUrl, data)
	}

	req.Header.Set("Content-Type", "application/json;charset=UTF-8")

	if url.Token != "" { // basic auth 认证
		req.Header.Set("Authorization", "Bearer "+url.Token)
	} else if (url.User != "") && (url.Password != "") {
		req.SetBasicAuth(url.User, url.Password)
	}

	response, _ := http.DefaultClient.Do(req) // 读取post返回的数据
	return response
}

func reponseToID(r *http.Response) int { // 网络请求转ID函数
	body, _ := io.ReadAll(r.Body)
	// 万能stuct体
	var data map[string]interface{}
	var id int
	// 解析http返回的json体
	if err := json.Unmarshal(body, &data); err == nil {
		for idx, value := range data {
			if idx == "id" {
				id = int(value.(float64))
				// log.Printf("AWX id:%v", id)
			}
		}
	}
	return id
}

type AwxConfigInfo struct {
	InstanceGrouId int
	EnvID          string
	CredentialId   int
}

// 调用Awx接口创建任务

type AwxInventoryHosts struct {
	Name      string `json:"name"`
	Variables string `json:"variables,omitempty"`
}

type FuncAwxLaunch struct {
	Type       string `json:"type"`       // playbook类型，例如prometheus-exporter等
	Tags       string `json:"tags"`       // 流水号
	DataCenter string `json:"datacenter"` // 所属数据中心
	Env        string `json:"env,omitempty"`
	Host       []AwxHostVar
	AppTeam    string `json:"app_team,omitempty"`
}
type AwxHostVar struct {
	IP  string
	Var string `json:"extra_vars,omitempty"`
}

// 随机一个sha256
func geneRandomString(length int) string {
	randomBytes := make([]byte, length/2)
	rand.Read(randomBytes)
	return hex.EncodeToString(randomBytes)
}

type CallAwx struct {
	FuncVar       FuncVar
	AwxConfigInfo AwxConfigInfo
}

/*
 * ------------------------------------
 *       调用Awx接口创建任务
 * ------------------------------------
 */
func (c CallAwx) LaunchJobforMonitorApply(v FuncAwxLaunch) int {
	// 1. 通过name获取 job template的ID
	log.Printf("INFO: Begin AWX, param type: %v, Tags: %v, Datacenter: %v, ENV: %v, HOST: %v.", v.Type, v.Tags, v.DataCenter, v.Env, v.Host)
	c.FuncVar.API = "/api/v2/job_templates/?name=" + v.Type // 初始化url结构体参数，不包括具体的API
	response := getData(c.FuncVar)                          // 获取template信息
	if response.StatusCode != 200 {                         // 错误处理
		log.Printf("ERROR: Failed to get ID of %s job_templates.", v.Type)
		panic("Failed to get job_templates ID")
	} else {
		log.Printf("INFO: Get ID success of %s job_templates.", v.Type)
	}
	body, _ := io.ReadAll(response.Body)
	var data map[string]interface{} // 万能stuct体
	var templateID int
	if err := json.Unmarshal(body, &data); err == nil { // 解析http返回的json体
		for idx, value := range data {
			if idx == "results" {
				dataMidd := value.([]interface{})
				dataResults := dataMidd[0].(map[string]interface{})
				for resultsIndex, resultsValue := range dataResults {
					if resultsIndex == "id" {
						templateID = int(resultsValue.(float64))
						break
					}
				}
				log.Printf("INFO: AWX Fetch template success, template: %v, templateID: %v", v.Type, templateID)
			}
		}
	}

	// 2. 创建inventory
	type AwxInventory struct {
		Name         string `json:"name"`
		Description  string `json:"description"`
		Organization int    `json:"organization"`
		Kind         string `json:"kind,omitempty"`
	}

	today := time.Now().Format("20060102")
	sha256String := geneRandomString(16)

	awxInventoryPara := AwxInventory{
		Name:         "VKPA-IV" + today + sha256String,
		Description:  "VKPA",
		Organization: 1,
	}

	c.FuncVar.API = "/api/v2/inventories/"     // 初始化url结构体参数，不包括具体的API
	bytes, _ := json.Marshal(awxInventoryPara) // struct数据转换成json格式的字节格式
	response = postData(c.FuncVar, bytes)      // 调用post函数
	inventoryID := reponseToID(response)
	if response.StatusCode == 201 {
		log.Printf("INFO: AWX Create inventory success, inventoryID: %v.", inventoryID)
	} else if response.StatusCode == 400 {
		body, _ := io.ReadAll(response.Body)
		log.Printf("ERROR: Failed to create inventory, error is %v.", string(body))
		panic("Failed to create inventory")
	} else {
		log.Printf("ERROR: Not have permission to create inventory.")
	}

	// 3. 为inventory创建instance_groups，关联实例组
	instanceGroupsID := c.AwxConfigInfo.InstanceGrouId
	type InstanceGroups struct {
		ID int `json:"id"`
	}
	awxInstanceGroupsPara := InstanceGroups{
		ID: instanceGroupsID,
	}
	c.FuncVar.API = "/api/v2/inventories/" + strconv.Itoa(inventoryID) + "/instance_groups/" // 初始化url结构体参数，不包括具体的API
	bytes, _ = json.Marshal(awxInstanceGroupsPara)
	response = postData(c.FuncVar, bytes) // 调用post函数
	if response.StatusCode != 204 && response.StatusCode != 201 {
		log.Printf("ERROR: AWX Failed to create instance groups for inventory.")
		panic("Failed to create instance groups for inventory")
	} else {
		log.Printf("INFO: AWX Create instance groups for inventory success, instance groups ID: %v.", instanceGroupsID)
	}

	// 4. 为inventory创建groups，创建组
	type AwxGroups struct {
		Name        string `json:"name"`
		Description string `json:"description"`
		Variables   string `json:"variables,omitempty"`
	}
	awxGroupsPara := AwxGroups{
		Name:        "VKPA-GP" + today + sha256String,
		Description: "VKPA",
	}
	c.FuncVar.API = "/api/v2/inventories/" + strconv.Itoa(inventoryID) + "/groups/" // 初始化url结构体参数，不包括具体的API
	bytes, _ = json.Marshal(awxGroupsPara)                                          // struct数据转换成json格式的字节格式
	response = postData(c.FuncVar, bytes)                                           // 调用post函数
	groupsID := reponseToID(response)
	if response.StatusCode == 201 {
		log.Printf("INFO: AWX Create groups for inventory success, groupsID: %v.", groupsID)
	} else if response.StatusCode == 400 {
		body, _ := io.ReadAll(response.Body)
		log.Printf("ERROR: Failed to create groups for inventory, error is %v.", string(body))
		panic("Failed to create groups for inventory")
	} else {
		log.Printf("WARNING: Do not have permission to create groups for inventory.")
	}

	// 5. 为groups创建hosts
	log.Printf("INFO: AWX Create hosts for groups.")
	tags := strings.ToLower(v.Tags)
	type AwxGroupsHosts struct {
		Name        string `json:"name"`
		Description string `json:"description"`
		Enabled     bool   `json:"enabled,omitempty"`
		InstanceId  string `json:"instance_id,omitempty"`
		Variables   string `json:"variables,omitempty"`
	}
	var groupsHosts []AwxGroupsHosts
	for _, keywordsApplyValue := range v.Host {
		var strKeywords string
		switch tags {
		case "process-new": ///进程新增场景
			arrStr := strings.Split(keywordsApplyValue.Var, "\n")
			if len(arrStr) > 1 { // 拼接变量信息
				var keywords string
				for i := 0; i < len(arrStr); i++ {
					var builder strings.Builder
					builder.WriteString("{\"include\": [\"")
					builder.WriteString(arrStr[i])
					builder.WriteString("\"]},")
					builder.WriteString(keywords)
					keywords = builder.String()
				}
				strKeywords = strings.TrimRight(keywords, ",")
			} else {
				var builder strings.Builder
				builder.WriteString("{\"include\": [\"")
				builder.WriteString(keywordsApplyValue.Var)
				builder.WriteString("\"]}")
				strKeywords = builder.String()
			}

			keywordVars := map[string]string{ // 生成json数据
				"process": "[" + strKeywords + "]",
			}
			keywordVarsJson, _ := json.Marshal(keywordVars)
			middVars := AwxGroupsHosts{
				Name:        keywordsApplyValue.IP,
				Description: "VKPA",
				Variables:   string(keywordVarsJson),
			}
			groupsHosts = append(groupsHosts, middVars)
		case "port-new": ///端口新增场景
			strKeywords := strings.ReplaceAll(keywordsApplyValue.Var, "\n", ",")
			// 生成注册json数据
			keywordVars := map[string]string{
				"ports": strKeywords,
			}
			keywordVarsJson, _ := json.Marshal(keywordVars)
			middVars := AwxGroupsHosts{
				Name:        keywordsApplyValue.IP,
				Description: "VKPA",
				Variables:   string(keywordVarsJson),
			}
			groupsHosts = append(groupsHosts, middVars)
		case "process-replace", "port-replace": ///替换场景
			strKeywords := strings.Split(keywordsApplyValue.Var, ",")
			// 生成注册json数据
			keywordVars := map[string]string{
				"sourcekeywords": strKeywords[0],
				"destkeywords":   strKeywords[1],
			}
			keywordVarsJson, _ := json.Marshal(keywordVars)
			middVars := AwxGroupsHosts{
				Name:        keywordsApplyValue.IP,
				Description: "VKPA",
				Variables:   string(keywordVarsJson),
			}
			groupsHosts = append(groupsHosts, middVars)

		case "process-delete", "port-delete": /// 删除场景
			middVars := AwxGroupsHosts{
				Name:        keywordsApplyValue.IP,
				Description: "VKPA",
			}
			if keywordsApplyValue.Var != "" {
				// 生成注册json数据
				keywordVars := map[string]string{
					"keywords": keywordsApplyValue.Var,
				}
				keywordVarsJson, _ := json.Marshal(keywordVars)
				middVars.Variables = string(keywordVarsJson)
			}

			groupsHosts = append(groupsHosts, middVars)

		case "node", "sys_process", "bdops": /// 推送agent场景
			middVars := AwxGroupsHosts{
				Name:        keywordsApplyValue.IP,
				Description: "VKPA",
			}
			groupsHosts = append(groupsHosts, middVars)

		default:
		}
	}

	c.FuncVar.API = "/api/v2/groups/" + strconv.Itoa(groupsID) + "/hosts/" // 初始化url结构体参数，不包括具体的API
	for _, host := range groupsHosts {                                     // 逐个IP添加到hosts
		bytes, _ = json.Marshal(&host)
		response = postData(c.FuncVar, bytes)
		if response.StatusCode != 201 {
			log.Printf("ERROR: Failed, do not have permission to add %v host", host)
			panic("Failed to add host")
		} else {
			log.Printf("INFO: Add host to group success.")
		}
	}

	// 6. 运行job
	monitorType := strings.Split(v.Tags, "-")

	var extraVarsJson []byte
	switch strings.ToLower(v.Tags) { // 根据不同的场景使用不同的运行变量
	case "process-new", "port-new", "node", "sys_process":
		launchExtraVars := map[string]string{
			"exporter_name": strings.ToLower(monitorType[0]) + "_exporter",
			"datacenter":    v.DataCenter,
			"env":           c.AwxConfigInfo.EnvID,
		}
		extraVarsJson, _ = json.Marshal(launchExtraVars)
	case "process-replace", "port-replace", "process-delete", "port-delete":
		launchExtraVars := map[string]string{
			"exporter_name": strings.ToLower(monitorType[0]),
			"datacenter":    v.DataCenter,
			"env":           c.AwxConfigInfo.EnvID,
			"type":          strings.ToLower(monitorType[1]),
		}
		extraVarsJson, _ = json.Marshal(launchExtraVars)
	case "bdops":
		launchExtraVars := map[string]interface{}{
			"team":       v.AppTeam,
			"datacenter": v.DataCenter,
			"env":        c.AwxConfigInfo.EnvID,
		}
		extraVarsJson, _ = json.Marshal(launchExtraVars)
	case "cancel":
	default:
	}

	log.Printf("INFO: AWX Create extraVarsJson for job success")

	credential := []int{c.AwxConfigInfo.CredentialId}
	type LaunchPara struct {
		AskLimitOnLaunch     bool   `json:"ask_limit_on_launch,omitempty"`
		AskScmBranchOnLaunch bool   `json:"ask_scm_branch_on_launch,omitempty"`
		ExtraVars            string `json:"extra_vars,omitempty"`
		Inventory            int    `json:"inventory,omitempty"`
		Limit                string `json:"limit,omitempty"`
		ScmBranch            string `json:"scm_branch,omitempty"`
		Credentials          []int  `json:"credentials,omitempty"`
	}
	launchPara := LaunchPara{
		ExtraVars:   string(extraVarsJson),
		Inventory:   inventoryID,
		Credentials: credential,
	}

	log.Printf("INFO: AWX launch param : %v", launchPara)

	awxLaunchPara := launchPara
	c.FuncVar.API = "/api/v2/job_templates/" + strconv.Itoa(templateID) + "/launch/" // 初始化url结构体参数，不包括具体的API
	bytes, _ = json.Marshal(awxLaunchPara)
	response = postData(c.FuncVar, bytes) // 调用post函数
	if response.StatusCode == 400 {
		body, _ := io.ReadAll(response.Body)
		log.Printf("ERROR: Failed to launch job, error is %v", string(body))
		panic("Failed to launch job")
	} else if response.StatusCode != 201 {
		log.Printf("ERROR: Do not have permission to launch job")
	}
	jobID := reponseToID(response)

	log.Printf("INFO: AWX launch jobs success, job id: %v", jobID)
	return jobID
}

func (c CallAwx) GetJobforMonitorApply(jobid int, tag string, dc string) string {

	// DB := sysReposi.GetDB()
	// trackNumberParts := strings.Split(tag, "-")

	// 7. 获取jobs 状态
	// jobState := c.GetJobs(jobid)
	c.FuncVar.API = "/api/v2/jobs/" + strconv.Itoa(jobid) + "/job_events/" // 初始化url结构体参数，不包括具体的API
	var state string
	var data map[string]interface{} // 万能stuct体
	for {
		time.Sleep(1 * time.Second)
		response := getData(c.FuncVar)
		body, _ := io.ReadAll(response.Body)
		var stdOut []string
		// 解析http返回的json体
		if err := json.Unmarshal(body, &data); err == nil {
			if data["detail"] == "Not found." {
				fmt.Println("Canot find Jobs")
				break
			}
			if data["count"] == nil {
				fmt.Println("count is nil")
			} else if data["count"].(float64) != 0 {

				dataResults := data["results"].([]interface{})

				for _, value := range dataResults {
					dataResultsSlice := value.(map[string]interface{})
					stdOut = append(stdOut, dataResultsSlice["stdout"].(string))

				}
				fmt.Println("stdOut", stdOut)
				dataResultsMap := dataResults[0].(map[string]interface{})
				dataResultsSummary := dataResultsMap["summary_fields"].(map[string]interface{})
				dataResultsSummaryJob := dataResultsSummary["job"].(map[string]interface{})

				if dataResultsSummaryJob["status"] == "failed" || dataResultsSummaryJob["status"] == "successful" {
					state = dataResultsSummaryJob["status"].(string)
					fmt.Println(state)
					break
				}
			}
		}
		time.Sleep(9 * time.Second)
	}
	//

	log.Printf("AWX:: jobs id: %v deploy finish state: %v", jobid, state)
	// DB.Table(strings.ToLower(trackNumberParts[1])+"_track_numbers_"+dc).Where("track_number = ?", tag).Update("deploy_state", state)
	return state
}

func (c CallAwx) GetJobEventsforMonitorApply(jobid int, tag string, dc string) (string, string) {

	// DB := sysReposi.GetDB()

	// 7. 获取jobs 状态
	// jobState, stdOut := c.GetJobsEvent(jobid)

	c.FuncVar.API = "/api/v2/jobs/" + strconv.Itoa(jobid) + "/job_events/" // 初始化url结构体参数，不包括具体的API
	var data map[string]interface{}                                        // 万能stuct体
	var state string
	response := getData(c.FuncVar)
	body, _ := io.ReadAll(response.Body)
	var stdOut string

	if err := json.Unmarshal(body, &data); err == nil { // 解析http返回的json体
		if data["detail"] == "Not found." {
			fmt.Println("Canot find Jobs")
		}
		if data["count"] == nil {
			fmt.Println("count is nil")
		} else if data["count"].(float64) != 0 {

			dataResults := data["results"].([]interface{})

			for _, value := range dataResults {
				dataResultsSlice := value.(map[string]interface{})
				fmt.Println(dataResultsSlice["stdout"].(string))
				stdOut = stdOut + dataResultsSlice["stdout"].(string)
			}
			dataResultsMap := dataResults[0].(map[string]interface{})
			dataResultsSummary := dataResultsMap["summary_fields"].(map[string]interface{})
			dataResultsSummaryJob := dataResultsSummary["job"].(map[string]interface{})

			if dataResultsSummaryJob["status"] == "failed" || dataResultsSummaryJob["status"] == "successful" {
				state = dataResultsSummaryJob["status"].(string)
				fmt.Println(state)
			}
		}
	}

	log.Printf("AWX:: jobs id: %v deploy finish state: %v", jobid, state)
	return state, stdOut

	// DB.Table(strings.ToLower(tag)+"_track_numbers_"+dc).Where("job_id = ?", jobid).Update("deploy_state", jobState)

	// return stdOut
}
